package client.net.io;

import client.Client;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * This class handles reading in input from the server.
 * 
 * This blocks while waiting for more bytes from server, so
 * it is run in a different thread. It processes packets as
 * they arrive.
 *
 * @author Vincent Tsuei
 * @version 2012-11-16
 */
public class ClientInputHandler implements Runnable {

    private boolean running = true;
    private final Client client;
    private final InputStream is;
    private final ByteBuffer readBuffer = ByteBuffer.allocate(4096);

    public ClientInputHandler(Client client, InputStream is) {
        this.client = client;
        this.is = new BufferedInputStream(is);
    }

    @Override
    public void run() {
        while (running) {
            try {
                //Read in the packet size.
                boolean readPacketSize = clearAndRead(4);
                if (readPacketSize) {
                    int packetSize = readBuffer.getInt();
                    boolean readPacket = clearAndRead(packetSize);
                    if (!readPacket) {
                        shutdown();
                    }
                    
                    // Read in data, then wrap in ByteBuffer for processing
                    byte[] packetData = new byte[packetSize];
                    readBuffer.get(packetData, 0, packetSize);
                    ByteBuffer packet = ByteBuffer.wrap(packetData);
                    
                    // Queue the packet for processing.
                    client.queuePacketForProcessing(packet);
                    
                } else {
                    shutdown();
                }
            } catch (IOException ex) {
                //If this happens. We are in trouble. Can't verify integrity of anything.
                //TODO: Figure out what to do. Best move might just be to error out of the client.
                if (ex.getMessage().equals("Connection reset")) {
                   Logger.getLogger(ClientInputHandler.class.getName()).log(Level.INFO, "The server has terminated the connection (It may be shutting down).");
                } else {
                   Logger.getLogger(ClientInputHandler.class.getName()).log(Level.SEVERE, "IOException in ClientInputHandler", ex);
                }
                running = false;
                shutdown();
            }
        }


    }

    /**
     * Clears the read buffer and reads in more data.
     *
     * Reads the specified number of bytes in. If -1 is read at any time, false
     * is returned immediately. There is no guarantee to the integrity of the
     * data in the readBuffer. True is returned if the specified number of bytes
     * is successfully read. Finally, flips the buffer so data can be read.
     */
    private boolean clearAndRead(int numBytes) throws IOException {
        readBuffer.clear();
        try {
            for (int i = 0; i < numBytes; i++) {
                int b = is.read();
                if (b == -1) {
                    return false;
                }
                readBuffer.put((byte) b);
            }
        } finally {
            readBuffer.flip();
        }
        return true;
    }
    
    /**
     * Shutdown this input handler.
     */
    public void shutdown() {
        running = false;
        client.getServerConnection().notifyInputShutdown();
    }
}
